#ifndef BACK_CHAINING_INFERENCE_HPP_GUARD
#define BACK_CHAINING_INFERENCE_HPP_GUARD

#include "strategy.hpp"
#include "kbase.hpp"

#include "utils.hpp"

#include "facts_searcher.hpp"
#include "rules_searcher.hpp"
#include "fact_with_mask_searcher.hpp"
#include "analogy_rules_searcher.hpp"

// #include "../utils/boost_pch.hpp"

#include <map>

#include <iostream>
#include <boost/cstdlib.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_as_tree.hpp>

namespace mashine { namespace inference {
/**
 * this class provide backward chaining strategy logic inference
 */
class BackwardChainingStrategy : public InferenceStrategy
{
public:
	/**
	 * param kBase - pointer to knowlege base
	 */
	BackwardChainingStrategy(mashine::kb::KnowledgeBase * kBase);
	
	/**
	 * 
	 */
	~BackwardChainingStrategy();
	
	/**
	 * backward chaining algorithm
	 *
	 * param goal - list of goals to inference
	 */
	virtual std::vector<kb::Literal> inference(std::vector<kb::Literal> const & goal);
	
private:
	mashine::kb::KnowledgeBase * kBase;
		
	std::vector<mashine::kb::Rule> analogyRulesPool;
	/**
	 * Visitors for search kEntity with any params
	 */
	mashine::kb::search::FactWithMaskSearchVisitor * factWithMaskSearcher;
	mashine::kb::search::RulesSearchVisitor * ruleSearcher;
	mashine::kb::search::AnalogyRulesSearchVisitor * aRuleSearcher;
		
	/**
	 * Properties of edge that can be obtained by PropertyMaps
	 */
	struct EdgeProperties {
		typedef boost::edge_property_tag kind;
	
		std::string operation;
	};
	
	/**
	 * Properties of vertex that can be obtained by PropertyMaps id, lit, params
	 */
	struct VertexProperties {
		typedef boost::vertex_property_tag kind;
		
		bool or_vertex;
		mashine::kb::Literal literal;
		std::vector<mashine::kb::Literal> facts;
	};
	
	// Base structure typedefs
	// 2 paramether in due time fuck up developers brain
	typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, VertexProperties,
			boost::no_property, boost::no_property, boost::vecS> Graph;
			
	typedef boost::graph_traits<Graph>::vertex_descriptor VertexDescriptor;
	
	typedef boost::graph_traits<Graph>::vertex_iterator VertexIterator;
	typedef boost::graph_traits<Graph>::out_edge_iterator EdgeIterator;
		
	Graph tree;
	VertexDescriptor root;
	
	typedef boost::property_map<Graph, mashine::kb::Literal VertexProperties::* >::type literal_map_t;
	typedef boost::property_map<Graph, std::vector<mashine::kb::Literal> VertexProperties::* >::type cortege_map_t;
	typedef boost::property_map<Graph, bool VertexProperties::* >::type node_type_map_t;
	
	literal_map_t literal_map;
	cortege_map_t cortege_map;
	node_type_map_t is_or_node_map;
	
	// For inference =))
	class tree_union_and_projection_visitor 
	{
	public:
		// typedef boost::tree_traits<Tree>::children_iterator ChildrenIterator;
	
		tree_union_and_projection_visitor() { }
				
		template <typename Node, typename Tree> 
		void preorder(Node n, Tree & t) { }
		
		template <typename Node, typename Tree>
		void inorder(Node n, Tree & t) { }

		// Left-Rigth-Root
		template <typename Node, typename Tree>
		void postorder(Node n, Tree & t)
		{
			// Node parent = boost::get(t.parent_pa(), n);
			//Node parent = boost::get(t.children(), n);
			
			//get(&VertexProperties::facts, t)[n] = (get(&VertexProperties::or_vertex, t)[n]) ? literalUnion(n, t) : literalConcat(n, t);
		}
		
	} union_and_projection;
	
	// For print tree
	class tree_print_visitor 
	{
	public:
		tree_print_visitor()
		{
			depth = 0;
		}
		
		template <typename Node, typename Tree> 
		void preorder(Node n, Tree & t) 
		{
			// std::cout << "(";
			
			if (depth > 0)
			{			
				int i = 1;
				while (i++ < depth)
				{
					std::cout << "     ";
				}
				std::cout << "+----";
			}
			
			std::cout << "{ " << get(&VertexProperties::literal, t)[n].toString() << " }" << std::endl;
			depth++;
		}
		
		template <typename Node, typename Tree>
		void inorder(Node n, Tree& t)
		{
		}

		template <typename Node, typename Tree>
		void postorder(Node, Tree&)
		{
			depth--;
		}
	private:
		int depth;
		
	} printer;
		
	void unificationAndProduction(VertexDescriptor const & root, std::vector<mashine::kb::Rule *> usedRules);
	void unionAndConcatenation(VertexDescriptor & root);
	
	/**
	 * this method call only for OR vertex
	 */ 
	void literalUnion(VertexDescriptor & root);
	
	/**
	 * this method call only for AND vertex
	 */ 
	void literalConcat(VertexDescriptor & root);
	std::vector<std::map<std::string, std::string> > concatenation_impl(EdgeIterator & b, EdgeIterator & e, 
			std::map<std::string, std::string> m);
	
	typedef boost::iterator_property_map<std::vector<VertexDescriptor>::iterator,
			boost::property_map<Graph, boost::vertex_index_t>::type> ParentMap;
			
	typedef boost::graph_as_tree<Graph, ParentMap> Tree;	
	
	void print();
};

}}

#endif